package com.j256.ormlite.hmos;

import com.j256.ormlite.dao.ObjectCache;
import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.db.SqlLiteHarmonyDatabaseType;
import com.j256.ormlite.support.DatabaseResults;
import com.j256.ormlite.utils.Constants;

import ohos.data.resultset.ResultSet;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * HmosDatabaseResults
 *
 */
public class HmosDatabaseResults implements DatabaseResults {
    private static final DatabaseType DATABASE_TYPE = new SqlLiteHarmonyDatabaseType();
    /**
     * Raw directory name that we are looking for.
     */
    private static final int MIN_NUM_COLUMN_NAMES_MAP = 8;
    private final ResultSet cursor;
    private final String[] columnNames;
    private final Map<String, Integer> columnNameMap;
    private final ObjectCache objectCache;
    private final boolean isCacheStore;

    /**
     * HmosDatabaseResults
     *
     * @param cursor ResultSet
     * @param objectCache ObjectCache
     * @param isCacheStore Boolean
     */
    public HmosDatabaseResults(ResultSet cursor, ObjectCache objectCache, boolean isCacheStore) {
        this.cursor = cursor;
        this.columnNames = cursor.getAllColumnNames();
        if (this.columnNames.length >= MIN_NUM_COLUMN_NAMES_MAP) {
            this.columnNameMap = new HashMap<String, Integer>();
            for (int index = 0; index < this.columnNames.length; index++) {
                this.columnNameMap.put(this.columnNames[index], index);
            }
        } else {
            columnNameMap = null;
        }
        this.objectCache = objectCache;
        this.isCacheStore = isCacheStore;
    }

    @Override
    public int getColumnCount() {
        return cursor.getColumnCount();
    }

    @Override
    public String[] getColumnNames() {
        int colN = getColumnCount();
        String[] columnnames = new String[colN];
        for (int colc = 0; colc < colN; colc++) {
            columnnames[colc] = cursor.getColumnNameForIndex(colc);
        }
        return columnnames;
    }

    @Override
    public boolean first() {
        return cursor.goToFirstRow();
    }

    @Override
    public boolean next() {
        return cursor.goToNextRow();
    }

    @Override
    public boolean last() {
        return cursor.goToLastRow();
    }

    @Override
    public boolean previous() {
        return cursor.goToPreviousRow();
    }

    @Override
    public boolean moveRelative(int offset) {
        return cursor.goTo(offset);
    }

    @Override
    public boolean moveAbsolute(int position) {
        return cursor.goToRow(position);
    }

    /**
     * Returns the count of results from the cursor.
     *
     * @return cursor
     */
    public int getCount() {
        return cursor.getRowCount();
    }

    /**
     * Returns the position of the cursor in the list of results.
     *
     * @return cursor
     */
    public int getPosition() {
        return cursor.getRowIndex();
    }

    @Override
    public int findColumn(String columnName) throws SQLException {
        int index = lookupColumn(columnName);
        if (index >= 0) {
            return index;
        }
        StringBuilder sb = new StringBuilder(columnName.length() + Constants.MAGIC_4);
        DATABASE_TYPE.appendEscapedEntityName(sb, columnName);
        index = lookupColumn(sb.toString());
        if (index >= 0) {
            return index;
        } else {
            String[] allColumnNames = cursor.getAllColumnNames();
            throw new SQLException("Unknown field '" + columnName + "' from the sqlite cursor, not in:"
                    + Arrays.toString(allColumnNames));
        }
    }

    @Override
    public String getString(int columnIndex) {
        return cursor.getString(columnIndex);
    }

    @Override
    public boolean getBoolean(int columnIndex) {
        if (cursor.isColumnNull(columnIndex) || cursor.getShort(columnIndex) == 0) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public char getChar(int columnIndex) throws SQLException {
        String string = cursor.getString(columnIndex);
        if (string == null || string.length() == 0) {
            return 0;
        } else if (string.length() == 1) {
            return string.charAt(0);
        } else {
            throw new SQLException("More than 1 character stored in database column: " + columnIndex);
        }
    }

    @Override
    public byte getByte(int columnIndex) {
        return (byte) getShort(columnIndex);
    }

    @Override
    public byte[] getBytes(int columnIndex) {
        return cursor.getBlob(columnIndex);
    }

    @Override
    public short getShort(int columnIndex) {
        return cursor.getShort(columnIndex);
    }

    @Override
    public int getInt(int columnIndex) {
        return cursor.getInt(columnIndex);
    }

    @Override
    public long getLong(int columnIndex) {
        return cursor.getLong(columnIndex);
    }

    @Override
    public float getFloat(int columnIndex) {
        return cursor.getFloat(columnIndex);
    }

    @Override
    public double getDouble(int columnIndex) {
        return cursor.getDouble(columnIndex);
    }

    @Override
    public Timestamp getTimestamp(int columnIndex) throws SQLException {
        throw new SQLException("does not support timestamp. Use JAVA_DATE_LONG or JAVA_DATE_STRING types");
    }

    @Override
    public InputStream getBlobStream(int columnIndex) {
        return new ByteArrayInputStream(cursor.getBlob(columnIndex));
    }

    @Override
    public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
        throw new SQLException(
                "does not support BigDecimal type. Use BIG_DECIMAL or BIG_DECIMAL_STRING types");
    }

    @Override
    public Object getObject(int columnIndex) throws SQLException {
        throw new SQLException("does not support Object type.");
    }

    @Override
    public boolean wasNull(int columnIndex) {
        return cursor.isColumnNull(columnIndex);
    }

    @Override
    public ObjectCache getObjectCacheForRetrieve() {
        return objectCache;
    }

    @Override
    public ObjectCache getObjectCacheForStore() {
        if (isCacheStore) {
            return objectCache;
        } else {
            return null;
        }
    }

    @Override
    public void close() {
        cursor.close();
    }

    @Override
    public void closeQuietly() {
        close();
    }

    /***
     * Returns the underlying cursor object. This should not be used unless you know what you are doing.
     *
     * @return cursor
     */
    public ResultSet getRawCursor() {
        return cursor;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode());
    }

    private int lookupColumn(String columnName) {
        if (columnNameMap == null) {
            for (int index = 0; index < columnNames.length; index++) {
                if (columnNames[index].equals(columnName)) {
                    return index;
                }
            }
            return Constants.MAGIC_MINUS_ONE;
        } else {
            Integer index = columnNameMap.get(columnName);
            if (index == null) {
                return Constants.MAGIC_MINUS_ONE;
            } else {
                return index;
            }
        }
    }
}
